/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus

This file is part of Tremulous.

Tremulous is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Tremulous is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Tremulous; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

// cg_marks.c -- wall marks


#include "cg_local.h"

/*
===================================================================

MARK POLYS

===================================================================
*/


markPoly_t      cg_activeMarkPolys;	// double linked list
markPoly_t     *cg_freeMarkPolys;	// single linked list
markPoly_t      cg_markPolys[MAX_MARK_POLYS];
static int      markTotal;

/*
===================
CG_InitMarkPolys

This is called at startup and for tournement restarts
===================
*/
void CG_InitMarkPolys(void)
{
	int             i;

	memset(cg_markPolys, 0, sizeof(cg_markPolys));

	cg_activeMarkPolys.nextMark = &cg_activeMarkPolys;
	cg_activeMarkPolys.prevMark = &cg_activeMarkPolys;
	cg_freeMarkPolys = cg_markPolys;

	for(i = 0; i < MAX_MARK_POLYS - 1; i++)
		cg_markPolys[i].nextMark = &cg_markPolys[i + 1];
}


/*
==================
CG_FreeMarkPoly
==================
*/
void CG_FreeMarkPoly(markPoly_t * le)
{
	if(!le->prevMark)
		CG_Error("CG_FreeLocalEntity: not active");

	// remove from the doubly linked active list
	le->prevMark->nextMark = le->nextMark;
	le->nextMark->prevMark = le->prevMark;

	// the free list is only singly linked
	le->nextMark = cg_freeMarkPolys;
	cg_freeMarkPolys = le;
}

/*
===================
CG_AllocMark

Will allways succeed, even if it requires freeing an old active mark
===================
*/
markPoly_t     *CG_AllocMark(void)
{
	markPoly_t     *le;
	int             time;

	if(!cg_freeMarkPolys)
	{
		// no free entities, so free the one at the end of the chain
		// remove the oldest active entity
		time = cg_activeMarkPolys.prevMark->time;

		while(cg_activeMarkPolys.prevMark && time == cg_activeMarkPolys.prevMark->time)
			CG_FreeMarkPoly(cg_activeMarkPolys.prevMark);
	}

	le = cg_freeMarkPolys;
	cg_freeMarkPolys = cg_freeMarkPolys->nextMark;

	memset(le, 0, sizeof(*le));

	// link into the active list
	le->nextMark = cg_activeMarkPolys.nextMark;
	le->prevMark = &cg_activeMarkPolys;
	cg_activeMarkPolys.nextMark->prevMark = le;
	cg_activeMarkPolys.nextMark = le;
	return le;
}



/*
=================
CG_ImpactMark

origin should be a point within a unit of the plane
dir should be the plane normal

temporary marks will not be stored or randomly oriented, but immediately
passed to the renderer.
=================
*/
#define MAX_MARK_FRAGMENTS  128
#define MAX_MARK_POINTS   384

void CG_ImpactMark(qhandle_t markShader, const vec3_t origin, const vec3_t dir,
				   float orientation, float red, float green, float blue, float alpha,
				   qboolean alphaFade, float radius, qboolean temporary)
{
	vec3_t          axis[3];
	float           texCoordScale;
	vec3_t          originalPoints[4];
	byte            colors[4];
	int             i, j;
	int             numFragments;
	markFragment_t  markFragments[MAX_MARK_FRAGMENTS], *mf;
	vec3_t          markPoints[MAX_MARK_POINTS];
	vec3_t          projection;

	if(!cg_addMarks.integer)
		return;

	if(radius <= 0)
		CG_Error("CG_ImpactMark called with <= 0 radius");

	//if ( markTotal >= MAX_MARK_POLYS ) {
	//  return;
	//}

	// create the texture axis
	VectorNormalize2(dir, axis[0]);
	PerpendicularVector(axis[1], axis[0]);
	RotatePointAroundVector(axis[2], axis[0], axis[1], orientation);
	CrossProduct(axis[0], axis[2], axis[1]);

	texCoordScale = 0.5 * 1.0 / radius;

	// create the full polygon
	for(i = 0; i < 3; i++)
	{
		originalPoints[0][i] = origin[i] - radius * axis[1][i] - radius * axis[2][i];
		originalPoints[1][i] = origin[i] + radius * axis[1][i] - radius * axis[2][i];
		originalPoints[2][i] = origin[i] + radius * axis[1][i] + radius * axis[2][i];
		originalPoints[3][i] = origin[i] - radius * axis[1][i] + radius * axis[2][i];
	}

	// get the fragments
	VectorScale(dir, -20, projection);
	numFragments = trap_CM_MarkFragments(4, (void *)originalPoints,
										 projection, MAX_MARK_POINTS, markPoints[0], MAX_MARK_FRAGMENTS, markFragments);

	colors[0] = red * 255;
	colors[1] = green * 255;
	colors[2] = blue * 255;
	colors[3] = alpha * 255;

	for(i = 0, mf = markFragments; i < numFragments; i++, mf++)
	{
		polyVert_t     *v;
		polyVert_t      verts[MAX_VERTS_ON_POLY];
		markPoly_t     *mark;

		// we have an upper limit on the complexity of polygons
		// that we store persistantly
		if(mf->numPoints > MAX_VERTS_ON_POLY)
			mf->numPoints = MAX_VERTS_ON_POLY;

		for(j = 0, v = verts; j < mf->numPoints; j++, v++)
		{
			vec3_t          delta;

			VectorCopy(markPoints[mf->firstPoint + j], v->xyz);

			VectorSubtract(v->xyz, origin, delta);
			v->st[0] = 0.5 + DotProduct(delta, axis[1]) * texCoordScale;
			v->st[1] = 0.5 + DotProduct(delta, axis[2]) * texCoordScale;
			*(int *)v->modulate = *(int *)colors;
		}

		// if it is a temporary (shadow) mark, add it immediately and forget about it
		if(temporary)
		{
			trap_R_AddPolyToScene(markShader, mf->numPoints, verts);
			continue;
		}

		// otherwise save it persistantly
		mark = CG_AllocMark();
		mark->time = cg.time;
		mark->alphaFade = alphaFade;
		mark->markShader = markShader;
		mark->poly.numVerts = mf->numPoints;
		mark->color[0] = red;
		mark->color[1] = green;
		mark->color[2] = blue;
		mark->color[3] = alpha;
		memcpy(mark->verts, verts, mf->numPoints * sizeof(verts[0]));
		markTotal++;
	}
}


/*
===============
CG_AddMarks
===============
*/
#define MARK_TOTAL_TIME   10000
#define MARK_FADE_TIME    1000

void CG_AddMarks(void)
{
	int             j;
	markPoly_t     *mp, *next;
	int             t;
	int             fade;

	if(!cg_addMarks.integer)
		return;

	mp = cg_activeMarkPolys.nextMark;
	for(; mp != &cg_activeMarkPolys; mp = next)
	{
		// grab next now, so if the local entity is freed we
		// still have it
		next = mp->nextMark;

		// see if it is time to completely remove it
		if(cg.time > mp->time + MARK_TOTAL_TIME)
		{
			CG_FreeMarkPoly(mp);
			continue;
		}

		// fade all marks out with time
		t = mp->time + MARK_TOTAL_TIME - cg.time;
		if(t < MARK_FADE_TIME)
		{
			fade = 255 * t / MARK_FADE_TIME;
			if(mp->alphaFade)
			{
				for(j = 0; j < mp->poly.numVerts; j++)
					mp->verts[j].modulate[3] = fade;
			}
			else
			{
				for(j = 0; j < mp->poly.numVerts; j++)
				{
					mp->verts[j].modulate[0] = mp->color[0] * fade;
					mp->verts[j].modulate[1] = mp->color[1] * fade;
					mp->verts[j].modulate[2] = mp->color[2] * fade;
				}
			}
		}

		trap_R_AddPolyToScene(mp->markShader, mp->poly.numVerts, mp->verts);
	}
}
